package com.hua.utility.encrypt;

import java.io.IOException;
import java.security.Key;
import java.util.Date;
import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class UtilEncryptReversComplex {

	private static String encryptType = "PBEWithMD5AndDES";

	private static final String randomString = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

	private static int encryptCodeLength = 5;

	private static String separatorStr = "Ⅹ";

	private static String getRandomString() {
		Random random = new Random();
		int range = randomString.length();
		String str = "";
		for (int i = 0; i < encryptCodeLength; i++) {
			str += randomString.charAt(random.nextInt(range));
		}
		return str;
	}

	/**
	 * 盐初始化
	 * 
	 * @return
	 * @throws Exception
	 */
	private static byte[] initSalt() throws Exception {
		byte[] salt = new byte[8];
		Random random = new Random();
		random.nextBytes(salt);
		return salt;
	}

	/**
	 * 转换密钥<br>
	 * 
	 * @param password
	 * @return
	 * @throws Exception
	 */
	private static Key toKey(String password) throws Exception {
		PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptType);
		SecretKey secretKey = keyFactory.generateSecret(keySpec);

		return secretKey;
	}

	/**
	 * 加密
	 * 
	 * @param data
	 *            数据
	 * @param password
	 *            密码
	 * @param salt
	 *            salt
	 * @return
	 * @throws Exception
	 */
	private static byte[] encryptPBE(byte[] data, String password, byte[] salt) throws Exception {

		Key key = toKey(password);

		PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
		Cipher cipher = Cipher.getInstance(encryptType);
		cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);

		return cipher.doFinal(data);

	}

	/**
	 * 解密
	 * 
	 * @param data
	 *            数据
	 * @param password
	 *            密码
	 * @param salt
	 *            salt
	 * @return
	 * @throws Exception
	 */
	private static byte[] decryptPBE(byte[] data, String password, byte[] salt) throws Exception {

		Key key = toKey(password);

		PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
		Cipher cipher = Cipher.getInstance(encryptType);
		cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

		return cipher.doFinal(data);

	}

	/**
	 * BASE64加密(可逆加密)
	 * 
	 * BASE64 严格地说，属于编码格式，而非加密算法
	 * 
	 * @param key
	 * @return
	 * 
	 */
	private static String encryptBASE64(byte[] key) {
		return (new BASE64Encoder()).encodeBuffer(key);
	}

	/**
	 * BASE64解密
	 * 
	 * @param key
	 * @return
	 * @throws IOException
	 * 
	 */
	private static byte[] decryptBASE64(String key) throws IOException {
		return (new BASE64Decoder()).decodeBuffer(key);
	}

	/**
	 * PBEWithMD5AndDES 和 BASE64 的加密
	 * 
	 * @param oldPwd
	 * @return
	 */
	public static String encrypt(String oldPwd) {
		StringBuffer newPwd = new StringBuffer("");
		try {
			byte[] oldPwdArr = oldPwd.getBytes();
			String encryptCode = getRandomString() + (new Date()).getTime();
			byte[] salt = initSalt();
			String saltValue = encryptBASE64(salt);
			byte[] encryptedData = encryptPBE(oldPwdArr, encryptCode, salt);
			String encryptPwd = encryptBASE64(encryptedData);

			newPwd.append(encryptBASE64(encryptCode.getBytes())).append(separatorStr)
					.append(encryptBASE64(encryptPwd.getBytes())).append(separatorStr)
					.append(encryptBASE64(saltValue.getBytes()));

		} catch (Exception e) {
			e.printStackTrace();
		}
		return encryptBASE64(newPwd.toString().getBytes()).replaceAll("\n", "").replaceAll("\r", "");
	}

	/**
	 * PBEWithMD5AndDES 和 BASE64 解密
	 * 
	 * @param newPwd
	 * @return
	 */
	public static String decrypt(String newPwd) {
		String oldPwd = "";
		String decryptNewPwd;
		try {
			decryptNewPwd = new String(decryptBASE64(newPwd));

			String code[] = decryptNewPwd.split(separatorStr);
			String encryptCode = new String(decryptBASE64(code[0]));
			byte[] salt = decryptBASE64(new String(decryptBASE64(code[2])));
			byte[] encryptedData = decryptBASE64(new String(decryptBASE64(code[1])));

			byte[] decryptPwd = decryptPBE(encryptedData, encryptCode, salt);
			oldPwd = new String(decryptPwd);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return oldPwd;
	}

	/**
	 * 
	 * PBEWithMD5AndDES 和 BASE64 密码比较
	 * 
	 * @param newPwd
	 * @param encryptPwd
	 * @return
	 */
	public static boolean compareEncrypt(String newPwd, String encryptPwd) {
		String oldPwd = decrypt(encryptPwd);
		if (oldPwd != null && oldPwd.equals(newPwd)) {
			return true;
		}
		return false;
	}

}